#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>

//Refere-se ao numero de puntos total
#define NPONTOS 10000000
//Refere-se ao numero de testes para promediar seus resultados
#define NTESTES 1

typedef double myfloat_t;
typedef unsigned long __attribute__((aligned(64))) integer_t;
typedef unsigned int __attribute__((aligned(64))) int_t;

//Agrupacion de parametros para cada thread
typedef struct
{
	int_t idThread;
	integer_t nPontos; //Numero de pontos propio
	int_t seed; //Semilla para el pseudogenerador
	integer_t cuenta; //Numero de puntos dentro del cuarto de circulo
} __attribute__ ((aligned(64))) myObj;

void* metodoParalelo(void* threadData)
{	
	integer_t myPontos=((myObj*)threadData)->nPontos;
	int_t seed=((myObj*)threadData)->seed;
	integer_t cuenta=((myObj*)threadData)->cuenta;
	    
	register integer_t I;
	myfloat_t coord_x,coord_y,radio;
	
	for(I=0;I<myPontos;++I)
	{
		coord_x=((myfloat_t)rand_r(&seed))/((myfloat_t)RAND_MAX);
		coord_y=((myfloat_t)rand_r(&seed))/((myfloat_t)RAND_MAX);
		
		radio=sqrt(coord_x*coord_x+coord_y*coord_y);
		
		if(radio<=1.0)
			++cuenta;
	}
	
	pthread_exit((void*)cuenta);
}

double metodoMontecarlo(integer_t nIter,int_t nHilos)
{
	integer_t I;
	pthread_t threads[nHilos];
	integer_t N=nIter/nHilos;

	myObj* data=(void*)malloc(sizeof(myObj)*nHilos);
	
	for(I=0;I<nHilos;++I)
	{
		data[I].idThread=I;
		data[I].nPontos=N;
		data[I].seed=rand();
		data[I].cuenta=0;

		int rc=pthread_create(&threads[I],NULL,metodoParalelo,(void *)&data[I]);
		if(rc)
		{
			 printf("Erro ao criar um novo thread: %d\n",rc);
			 return 1;
		}
	}
	
	integer_t contDentro=0;
	for(I=0;I<nHilos;++I)
	{
		integer_t tmp;
		pthread_join(threads[I],(void*)&tmp);
		contDentro+=tmp;
	}

	return 4.0*((myfloat_t)contDentro/(myfloat_t)nIter);
}

int main(int argc,char** argv)
{
	integer_t nPontos=NPONTOS;
	int_t nHilos=sysconf(_SC_NPROCESSORS_ONLN);
	int_t nTestes=NTESTES;

	if(argc>1)
		sscanf(argv[1],"%ld",&nPontos);
	if(argc>=3)
		sscanf(argv[2],"%d",&nHilos);
	if(argc==4)
		sscanf(argv[3],"%d",&nTestes);

	printf("%ld\t",nPontos);

	srand(time(0));
    
	double sumTempos=0.0;
	double sumValores=0.0;
	
	int_t I;
	for(I=0;I<nTestes;++I)
	{
		struct timespec ts1,ts2;
		clock_gettime(CLOCK_REALTIME,&ts1);
		
		sumValores+=metodoMontecarlo(nPontos,nHilos);

		clock_gettime(CLOCK_REALTIME,&ts2);
		sumTempos+=(float)(1.0*(1.0*ts2.tv_nsec-ts1.tv_nsec*1.0)*1e-9+1.0*ts2.tv_sec-1.0*ts1.tv_sec);
	}
	
	printf("%f\t%f\n",sumTempos/(double)nTestes,sumValores/(double)nTestes);
	
	pthread_exit(NULL);	
	return 0;
}
